package com.TT.common.unifiedLogPrint;


import com.TT.common.REST.RESTResponse;
import com.TT.common.REST.ResCode;
import com.TT.common.unifiedLogPrint.dictionary.OperationStatus;
import com.TT.common.unifiedLogPrint.dictionary.OprLogMsg;
import com.TT.common.utils.IPUtils;
import com.TT.domain.mysql.OprLog;
import com.TT.domain.mysql.User;
import com.TT.service.LogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Objects;


/**
 * 切面方法类
 * 处理统一日志打印和记录
 */
@Aspect
@Component
@Slf4j
public class LogAspect {
    @Autowired
    LogService logService;

    public LogAspect() {
        System.out.println("logaspect");
    }

    @Pointcut(value = "execution(* com.TT.service..*(..))")
    private void serviceCut() {
    }//切入点签名

    @Pointcut(value = "execution(* com.TT.controller..*(..))")
    private void controllerCut() {
    }//切入点签名

    /**
     * service执行时间
     *
     * @param pjp       执行体
     * @param recordLog 自定义log注解
     * @return
     * @throws Throwable
     */
    @Around(value = "controllerCut() && @annotation(recordLog)", argNames = "pjp,recordLog")
    public Object logServiceAround(ProceedingJoinPoint pjp, RecordLog recordLog) throws Throwable {
        System.out.println("--------------");
        long start = System.currentTimeMillis();
        Object object;
        HttpServletRequest request = getRequest(pjp);
        OprLogMsg oprLogMsg = new OprLogMsg(recordLog.level(), recordLog.oprationName());
        try {
            object = pjp.proceed();
            RESTResponse restResponse = (RESTResponse) object;
            //执行完成的时间
            long costTime = System.currentTimeMillis() - start;
            //rest响应 操作错误
            recordLogByDB(request, costTime, oprLogMsg, restResponse);

        } catch (Throwable e) {
            //未知错误
            recordLogByDB(request, System.currentTimeMillis() - start, oprLogMsg, RESTResponse.simple_fail());
            throw e;
        }
        return object;
    }


    /**
     * @Description: 将日志写入数据表, 并记录到文件
     * @author alien
     * @date 2016年10月24日 下午3:25:25
     * @version Version 1.0.1
     */
    private void recordLogByDB(HttpServletRequest request, long costTime, OprLogMsg oprLogMsg, RESTResponse e) {
        try {
            // 记录DB日志
            User loginUser = null;
            String url = "";
            String params = "";
            //获取用户信息
            if (request != null) {
                loginUser = (User) request.getSession().getAttribute("token");
                url = request.getRequestURL().toString();
                params = getParams(request);
                if (!ObjectUtils.anyNotNull(loginUser)) {
                    return;
                }
            }

            //获取url参数
            if (params.getBytes(StandardCharsets.UTF_8).length > 200) {
                params = params.substring(0, 199);
            } else if (params.endsWith("&")) {
                params = params.substring(0, params.length() - 2);
            }

            boolean b = e.getCode() == ResCode.Simple_SUCESS.getCode();
            short i = b ? OperationStatus.SUCCESS : OperationStatus.FAIL;
            String eMsg = b ? oprLogMsg.getMsg()
                    : e.getMsg().getBytes(StandardCharsets.UTF_8).length < 5000 ? oprLogMsg.getMsg()
                    + e.getMsg()
                    : oprLogMsg.getMsg() + e.getMsg().substring(0, 4999);

            String clientIp = IPUtils.getClientIp(Objects.requireNonNull(request));
            OprLog oprLog = new OprLog();
            oprLog.setUserId(loginUser.getUserId());
            oprLog.setClientIp(clientIp);
            oprLog.setServerIp(IPUtils.getServerIp());
            oprLog.setCostTime(costTime);
            oprLog.setLogLevel(oprLogMsg.getLevel().getLevel());
            oprLog.setOperationDesc(eMsg);
            oprLog.setOperationStatus(i);
            oprLog.setOperationName(log.getName());
            oprLog.setUrl(url);
            oprLog.setParams(params);
            logService.log(oprLog);
        } catch (Exception exception) {
            log.error("日志写入异常!", exception);
        }
    }

    /**
     * 从连接点获取request对象并且解析出log部分对象信息
     *
     * @param joinPoint
     * @return
     */
    public HttpServletRequest getRequest(JoinPoint joinPoint) throws Exception {
        HttpServletRequest request = null;
        if (null != RequestContextHolder.getRequestAttributes()) {
            request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            log.debug("Find {} from RequestContextHolder", request);
        }
        if (null == request) {
            Object[] getArgs = joinPoint.getArgs();// 返回目标方法的参数
            if (null != getArgs) {
                for (Object object : getArgs) {
                    if (object instanceof HttpServletRequest) {
                        request = (HttpServletRequest) object;
                        break;
                    }
                }
            }
        }
        return request;
    }

    /**
     * 从request中获取请求参数
     *
     * @param request
     * @return
     * @throws Exception
     */
    private String getParams(HttpServletRequest request) throws Exception {
        Enumeration<String> enu = request.getParameterNames();
        StringBuffer sb = new StringBuffer();
        while (enu.hasMoreElements()) {
            String paraName = (String) enu.nextElement();
            sb.append(paraName);
            sb.append("=");
            sb.append(request.getParameter(paraName));
            sb.append("&");
        }
        return sb.toString();
    }

}
